package uk.me.dewi.appenin.android.dao.rest.json;

import static uk.me.dewi.util.DateFormats.ISO_DATE_TIME_MILLIS_FORMAT;

import java.util.ArrayList;
import java.util.Collections;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import uk.me.dewi.appenin.android.dao.PlaceDao;
import uk.me.dewi.appenin.android.dao.rest.PlaceRestDao;
import uk.me.dewi.appenin.android.entity.Place;
import uk.me.dewi.net.rest.ResponseHandler;
import uk.me.dewi.net.rest.RestException;
import uk.me.dewi.util.ArrayUtils;

public class PlaceJsonRestDao extends PlaceRestDao implements PlaceDao {

    public PlaceJsonRestDao(String baseUrl) {
        super(new JsonRequestFactory(baseUrl));
    }

    public Place createPlace(String path, Place place) throws RestException {
        String data = toJson(place).toString();
        Long id = (Long) mRestClient.put(path, data, new ResponseHandler(){
            public Object handleResponse(String resultString) throws Exception{
                JSONObject result = new JSONObject(resultString);
                if(result.has("id")){
                    try{
                        return result.getLong("id");
                    }catch(JSONException e){
                        throw new RestException("Cannot parse id", e);
                    }
                }
                return null;
            }
        });
        if(id != null) {
            place.mId = id.longValue();
            return place;
        }
        throw new RestException("Could not create "+data);
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#deletePlace(long)
     */
    public boolean deletePlace(String path) throws RestException {
        throw new RuntimeException("NOT IMPLEMENTED");
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#getNearbyPlaces(com.google.android.maps.GeoPoint, int)
     */
    @SuppressWarnings("unchecked")
    public ArrayList<Place> getNearbyPlaces(String path) throws RestException {
        return (ArrayList<Place>) mRestClient.get(path, new ResponseHandler() {
            public Object handleResponse(String resultString) throws Exception{
                JSONObject result = new JSONObject(resultString);
                if(result.has("places")){
                    JSONArray placesJson = result.getJSONArray("places");
                    ArrayList<Place> places = new ArrayList<Place>(placesJson.length());
                    for(int i=0; i<placesJson.length(); i++){
                        places.add(
                                populate(new Place(), 
                                        placesJson.getJSONObject(i))
                                        .validate());
                    }
                    return places;
                }
                return Collections.emptyList();
            }
        });
    }
    
    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#getNearbyPlaceIDs(com.google.android.maps.GeoPoint, int)
     */
    public long[] getNearbyPlaceIDs(String path) throws RestException {
        throw new RuntimeException("NOT IMPLEMENTED");
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#fetchPlace(long)
     */
    public Place fetchPlace(String path) throws RestException {
        Place place = (Place) mRestClient.get(path, new ResponseHandler(){
            public Object handleResponse(String resultString) throws Exception{
                JSONObject result = new JSONObject(resultString);
                if(result.has("place")){
                    return populate(new Place(), result).validate();
                }
                return null;
            }
        });

        return place;
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#updatePlace(uk.me.dewi.appenin.android.entity.Place)
     */
    public boolean updatePlace(String path, Place place) throws RestException {
        throw new RuntimeException("NOT IMPLEMENTED");
    }
    
    public Place populate(Place place, JSONObject json){
        try{
            JSONObject placeJson = json.getJSONObject("place");
            place.mRatings = placeJson.getInt("ratings");
            place.mDescription = placeJson.getString("description");
            place.mTags = placeJson.getString("tags").split(" ");
            place.mCreated = ISO_DATE_TIME_MILLIS_FORMAT.parse(placeJson.getString("created"));
            place.mModified = ISO_DATE_TIME_MILLIS_FORMAT.parse(placeJson.getString("modified"));
            place.mUser = placeJson.getString("user");
            place.mGeo = parseGeoString(placeJson.getString("geo"));
            place.mName = placeJson.getString("name");
            place.mId = placeJson.getLong("id");
            return place;
        } catch (Exception e) {
            throw new RuntimeException("Could not parse Place JSON "+json, e);
        }
    }
    
    /**
     * Converts this Place into a JSON object, e.g.:
      {place: {
        ratings: '10',
        description: 'This is some place',
        tags: 'some place',
        created: '2009-05-26T21:25:41.204466',
        modified: '2009-05-26T21:25:41.205207',
        user: 'jinglemansweep@googlemail.com',
        geo: '51.0,1.1',
        name: 'Some Place'
      }
    **/
    public JSONObject toJson(Place place){
        place.validate();
        try {
            JSONObject data = new JSONObject()
                .put("ratings", place.mRatings)
                .put("description", place.mDescription)
                .put("tags", ArrayUtils.join(place.mTags, " "))
                .put("created", ISO_DATE_TIME_MILLIS_FORMAT.format(place.mCreated))
                .put("modified", ISO_DATE_TIME_MILLIS_FORMAT.format(place.mModified))
                .put("user", place.mUser)
                .put("geo", place.getGeoString())
                .put("name", place.mName);
            if(place.mId != null){
                data.put("id", place.mId);
            }
                
            return new JSONObject().put("place", data);
        } catch (JSONException e) {
            throw new RuntimeException("Could not convert "+toString()+" to JSON", e);
        }
    }

}
